package com.nali.dal.mongo.explain.query;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

/**
 * PreparedFind<br>&nbsp;
 * 为各种实现类提供统一的调用接口
 * @author sam Created on 2011-3-8
 */
public interface PreparedFind {
	List<Map<String, Object>> find(List<Object> params, DBCollection collection, int start, int limit);
	long count(List<Object> params, DBCollection collection);
}
class NormalFind implements PreparedFind {
	private static Logger logger = Logger.getLogger(NormalFind.class);
	private Condition condition;
	private ResultProperties properties;
	public NormalFind(Condition condition, ResultProperties properties) {
		super();
		this.condition = condition;
		this.properties = properties;
	}
	@Override
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> find(List<Object> params, DBCollection collection, int start, int limit) {
		DBObject conds = condition.getQueryDBObject(params);
		DBObject keys = properties.getKeys(params, condition.getParamsLength());
		if(logger.isDebugEnabled()) {
			logger.debug("collection:" + collection.getName());
			logger.debug("query:" + conds);
			logger.debug("keys:" + keys);
		}
		DBCursor ql = collection.find(conds, keys).skip(start).limit(limit);
		List<Map<String, Object>> rlt=new ArrayList<Map<String,Object>>();
		while(ql.hasNext()) {
			rlt.add(ql.next().toMap());
		}
		if(logger.isDebugEnabled()) {
			logger.debug("result size:" +rlt.size());
		}
		return rlt;
	}
	@Override
	public long count(List<Object> params, DBCollection collection) {
		DBObject conds = condition.getQueryDBObject(params);
		if(logger.isDebugEnabled()) {
			logger.debug("count:" + collection.getName());
			logger.debug("query:" + conds);
		}
		return collection.count(conds);
	}
}
class OrderByFind implements PreparedFind {
	private static Logger logger = Logger.getLogger(OrderByFind.class);
	private static final String $ORDERBY = "$orderby";
	private static final String $QUERY = "$query";
	private Condition condition;
	private ResultProperties properties;
	private DBObject orders;
	public OrderByFind(Condition condition, ResultProperties properties, DBObject orders) {
		super();
		this.condition = condition;
		this.properties = properties;
		this.orders = orders;
	}
	@Override
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> find(List<Object> params, DBCollection collection, int start, int limit) {
		DBObject conds = addOrder(condition.getQueryDBObject(params), orders);
		DBObject keys = properties.getKeys(params, condition.getParamsLength());
		if(logger.isDebugEnabled()) {
			logger.debug("collection:" + collection.getName());
			logger.debug("query:" + conds);
			logger.debug("keys:" + keys);
		}
		DBCursor cursor = collection.find(conds, keys);
		cursor.skip(start).limit(limit);
		List<DBObject> ql = cursor.toArray();
		List<Map<String, Object>> rlt = new ArrayList<Map<String,Object>>(ql.size());
		for (DBObject bson : ql) {
			rlt.add(bson.toMap());
		}
		if(logger.isDebugEnabled()) {
			logger.debug("result size:" +rlt.size());
		}
		return rlt ;
	}

	@SuppressWarnings("unused")
	private<T> List<T> subList(List<T> ql, int start, int limit) {
		start=Math.min(start, ql.size());
		start=Math.max(start, 0);
		int to=limit==0?ql.size():Math.min(start + limit, ql.size());
		return ql.subList(start, to);
	}
	
	private DBObject addOrder(DBObject query, DBObject orders) {
		return new BasicDBObject($QUERY, query).append($ORDERBY, orders);
	}
	@Override
	public long count(List<Object> params, DBCollection collection) {
		DBObject conds = condition.getQueryDBObject(params);
		if(logger.isDebugEnabled()) {
			logger.debug("count:" + collection.getName());
			logger.debug("query:" + conds);
		}
		return collection.count(conds);
	}
}
class GroupByFind implements PreparedFind {
	private static Logger logger = Logger.getLogger(GroupByFind.class);
	private DBObject groups;
	private Condition condition;
	private DBObject initial;
	private String reduce;

	public GroupByFind(DBObject groups, Condition condition, DBObject initial, String reduce) {
		super();
		this.groups = groups;
		this.condition = condition;
		this.initial = initial;
		this.reduce = reduce;
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> find(List<Object> params, DBCollection collection, int start, int limit) {
		DBObject conds = condition.getQueryDBObject(params);
		if(logger.isDebugEnabled()) {
			logger.debug("collection:" + collection.getName());
			logger.debug("keys:" + groups);
			logger.debug("query:" + conds);
			logger.debug("initial:" + initial);
			logger.debug("reduce:" + reduce);
		}
		List<DBObject> ql=(List<DBObject>) collection.group(groups, conds, initial, reduce);
		ql = subList(ql, start, limit);
		List<Map<String, Object>> rlt = new ArrayList<Map<String,Object>>(ql.size());
		for (DBObject bson : ql) {
			rlt.add(bson.toMap());
		}
		if(logger.isDebugEnabled()) {
			logger.debug("result size:" +rlt.size());
		}
		return rlt;
	}

	private<T> List<T> subList(List<T> ql, int start, int limit) {
		start=Math.min(start, ql.size());
		start=Math.max(start, 0);
		int to=limit==0?ql.size():Math.min(start + limit, ql.size());
		return ql.subList(start, to);
	}

	@Override
	public long count(List<Object> params, DBCollection collection) {
		logger.warn("count a groupBy query");
		return find(params, collection, 0, 0).size();
	}
}